#include "recordengine.h"
#include <fftw3.h>
#include <wx/textfile.h>
#include <wx/spinctrl.h>
using namespace std;

// input:
// - theFrequency = the frequency to compare it to
// - duration = the recording duration in milliseconds
recordEngine::recordEngine(float theFrequency, int duration, wxTextCtrl* theTxtDebug,
                           int theNoteNr, note* theNewNote, tabView *theTabViewInit, wxGauge *theGaugeInit, int theRecordType, wxSpinCtrl *theSpnTreshold) {
  spnTreshold = theSpnTreshold;
  recordType = theRecordType;
  theGauge = theGaugeInit;
  theTabView = theTabViewInit;
  theNote = theNewNote;
  txtDebug = theTxtDebug;
  sampleRate = 8000;
  theDuration = duration;
  NUMPTS = (int)(sampleRate * ((float)duration / 1000));
  noteNr = theNoteNr;
  //wxMessageBox(wxString::Format(_("%d"), NUMPTS));
  frequency = theFrequency;
  waveIn = new unsigned char[NUMPTS];

  WCHAR* fault;
   // Specify recording parameters
  //WAVEFORMATEX pFormat;
  pFormat.wFormatTag = WAVE_FORMAT_PCM;     // simple, uncompressed format
  pFormat.nChannels = 1;                    //  1=mono, 2=stereo
  pFormat.nSamplesPerSec = sampleRate;      // 44100
  pFormat.nAvgBytesPerSec = sampleRate;     // = nSamplesPerSec * n.Channels * wBitsPerSample/8
  pFormat.nBlockAlign = 1;                  // = n.Channels * wBitsPerSample/8
  pFormat.wBitsPerSample = 8;               //  16 for high quality, 8 for telephone-grade
  pFormat.cbSize = 0;

  result = waveInOpen(&hWaveIn, WAVE_MAPPER,&pFormat, 0L, 0L, WAVE_FORMAT_DIRECT);
  if(result) {
    waveInGetErrorText(result, fault, 256);
    wxString message = wxString(fault, wxSTRING_MAXLEN);
    wxMessageBox(message, wxT("Failed to open waveform input device."), wxOK|wxICON_INFORMATION);
    return;
  }

  // Set up and prepare header for input
  WaveInHdr.lpData = (LPSTR)waveIn;
  WaveInHdr.dwBufferLength = NUMPTS;
  WaveInHdr.dwBytesRecorded=0;
  WaveInHdr.dwUser = 0L;
  WaveInHdr.dwFlags = 0L;
  WaveInHdr.dwLoops = 0L;
  waveInPrepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));

   //Create efficient architecture-dependant fftw plan
  in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * WaveInHdr.dwBufferLength);
  out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * WaveInHdr.dwBufferLength);
  p = fftw_plan_dft_1d(WaveInHdr.dwBufferLength, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
}

void *recordEngine::Entry() {
  WCHAR* fault;
  double absval;

  // Insert a wave input buffer
  result = waveInAddBuffer(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
  if(result) {
    waveInGetErrorText(result, fault, 256);
    wxString message = wxString(fault, wxSTRING_MAXLEN);
    wxMessageBox(message, wxT("Failed to read block from device."), wxOK|wxICON_INFORMATION);
    return NULL;
  }

  // Commence sampling input
  result = waveInStart(hWaveIn);
  if(result){
    waveInGetErrorText(result, fault, 256);
    wxString message = wxString(fault, wxSTRING_MAXLEN);
    wxMessageBox(message, wxT("Failed to start recording."), wxOK|wxICON_INFORMATION);
    return NULL;
  }

  //int lastVal = -1;
  while( !(WaveInHdr.dwFlags & WHDR_DONE) ) {
    // nothing, recording
    /*if((int)WaveInHdr.dwBytesRecorded != lastVal) {
      theGauge->SetValue(min(100, (int)(3*((double)WaveInHdr.lpData[WaveInHdr.dwBytesRecorded])-127)));
      lastVal = WaveInHdr.dwBytesRecorded;
    }*/
  }

  /*wxTextFile outFile(wxString::Format(_("audio_%d.txt"), noteNr));
  if(outFile.Exists())
    outFile.Open();
  else
    outFile.Create();
  outFile.Clear();*/
  for(unsigned int i=0; i<WaveInHdr.dwBytesRecorded; i ++) {
    short int temp = (byte)(double)WaveInHdr.lpData[i];
    //temp = temp << 8;
    //temp += (byte)(double)WaveInHdr.lpData[i+1];
    //outFile.AddLine(wxString::Format(_("%d"), temp));
    in[i][0]=temp;
    in[i][1]=0.0;
    // set the gauge
    //if(i % 10 == 0)
      //theGauge->SetValue(min(100, 3*(temp-127)));
  }
  //outFile.Write();
  //outFile.Close();

  fftw_execute(p);

  wxTextFile outFile2(wxString::Format(_("note_%d.txt"), noteNr));
  if(outFile2.Exists())
    outFile2.Open();
  else
    outFile2.Create();
  outFile2.Clear();
	float frequencies[NUMPTS];
	for (int i = 0; i < NUMPTS >> 1; i++) {
	  if(i == 0)
	    absval = 0;
    else
      absval = sqrt(out[i][0] * out[i][0]+out[i][1]*out[i][1]);
		frequencies[i] = absval;
    outFile2.AddLine(wxString::Format(_("%f"), absval));
	}
	outFile2.Write();
  outFile2.Close();

  if(recordType == 0) {
    // normal checking
    int freqIndex = (int)(((float)(NUMPTS / 2) / 4000) * frequency + 0.5);
    float maxEnergy = max(frequencies[freqIndex - 2], max(frequencies[freqIndex - 1], frequencies[freqIndex]));
    maxEnergy = max(maxEnergy, frequencies[freqIndex + 1]);
    int searchEnergy = (int)(spnTreshold->GetValue() * 0.5 * ((float)theDuration / 1000));
    if(searchEnergy < 500)
      searchEnergy = 500;
    if(searchEnergy > 1500)
      searchEnergy = 1500;
    if(maxEnergy > searchEnergy) {
      txtDebug->AppendText(wxString::Format(_("  Correct! %f\t%d\t%d\t%f\t%d\n"), maxEnergy, (int)(frequency + 0.5), freqIndex, frequencies[freqIndex], noteNr));
      theNote->status = 2;
    } else {
      txtDebug->AppendText(wxString::Format(_("Incorrect! %f\t%d\t%d\t%f\t%d\t%d\n"), maxEnergy, (int)(frequency + 0.5), freqIndex, frequencies[freqIndex], noteNr, searchEnergy));
      theNote->status = 1;
    }
    theTabView->doUpdate();

  } else {
    // update treshold
    int freqIndex = (int)(((float)(NUMPTS >> 1) / 4000) * frequency + 0.5);
    int newThreshold = (int)(frequencies[freqIndex] * 0.2 + 0.5);
    txtDebug->AppendText(wxString::Format(_("New threshold: %d\n"), newThreshold));
    spnTreshold->SetValue(newThreshold);
  }

  //close mic
  waveInStop(hWaveIn);
  waveInUnprepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
  waveInClose(hWaveIn);
  fftw_destroy_plan(p);
  fftw_free(in);
  fftw_free(out);
  return NULL;
}

void recordEngine::OnExit() {
  // nothing for now
}

